﻿//--------------------------------------------------------------------------
// <copyright file="PipeFilter.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>PipeFilter</summary>
//--------------------------------------------------------------------------
namespace MS.Msn.MediaSolutions.Common
{
    using System;
    using System.ComponentModel;
    using System.Collections.Generic;

    /// <summary>
    /// PipeStepStatus
    /// </summary>
    public enum PipeStepStatus
    {
        /// <summary>
        /// unknown
        /// </summary>
        Unknown,
        /// <summary>
        /// initialized
        /// </summary>
        Initialized,
        /// <summary>
        /// canceled
        /// </summary>
        ProgressCanceled,
        /// <summary>
        /// finished
        /// </summary>
        ProgressFinished,
        /// <summary>
        /// completed
        /// </summary>
        PipeComplete
    }

    /// <summary>
    /// PipeFilter
    /// </summary>
    /// <typeparam name="T">context data object type</typeparam>
    public sealed class PipeFilter<T>
    {
        private PipeStep<T>[] steps;

        /// <summary>
        /// PipeHelper
        /// </summary>
        /// <param name="stepCount">total step</param>
        /// <param name="ctx">context (generic type)</param>
        public PipeFilter(int stepCount, T ctx)
        {
            initPipes(stepCount, ctx);
        }

        /// <summary>
        /// PipeFilter
        /// </summary>
        /// <param name="ctx">context (generic type)</param>
        /// <param name="handlers">handler delegate</param>
        public PipeFilter(T ctx, params PipeStep<T>.ProcessHandler[] handlers)
        {
            initPipes(handlers.Length, ctx);
            initHandlers(handlers);
        }

        /// <summary>
        /// initialize pipe
        /// </summary>
        /// <param name="stepCount">total step</param>
        /// <param name="ctx">context (generic type)</param>
        private void initPipes(int stepCount, T ctx)
        {
            if (stepCount > 0)
            {
                steps = new PipeStep<T>[stepCount];

                steps[stepCount - 1] = new PipeStep<T>(ctx);

                for (int i = stepCount - 2; i > -1; i--)
                {
                    steps[i] = new PipeStep<T>(ctx, steps[i + 1]);
                }
            }
            else
            {
                throw new ArgumentOutOfRangeException(Apps.GetArgNullExString("stepCount"));
            }
        }

        /// <summary>
        /// initialize handlers for each step
        /// </summary>
        /// <param name="handlers">handlers</param>
        private void initHandlers(params PipeStep<T>.ProcessHandler[] handlers)
        {
            int stpCount = steps.Length;
            int hdlCount = handlers.Length;

            for (int i = 0, l = Math.Min(stpCount, hdlCount); i < l; i++)
            {
                steps[i].Process = handlers[i];
            }
        }

        /// <summary>
        /// initialize cancelers for each step
        /// </summary>
        /// <param name="handlers">handlers</param>
        private void initCancelers(params Proc<T, bool>[] handlers)
        {
            int stpCount = steps.Length;
            int hdlCount = handlers.Length;

            for (int i = 0, l = Math.Min(stpCount, hdlCount); i < l; i++)
            {
                steps[i].CancelProgressProc = handlers[i];
            }
        }

        /// <summary>
        /// AttachStepHandlers
        /// </summary>
        /// <param name="handlers">handlers</param>
        public void AttachStepHandlers(params PipeStep<T>.ProcessHandler[] handlers)
        {
            initHandlers(handlers);
        }

        /// <summary>
        /// AttachStepCancelers
        /// </summary>
        /// <param name="handlers">handlers</param>
        public void AttachStepCancelers(params Proc<T, bool>[] handlers)
        {
            initCancelers(handlers);
        }

        /// <summary>
        /// Process Pipe
        /// </summary>
        /// <returns></returns>
        public T ProcessPipe()
        {
            if (!object.Equals(steps, null) && steps.Length > 0)
            {
                steps[0].StartStep();
                return steps[steps.Length - 1].ContextData;
            }
            else
            {
                throw new ArgumentNullException(Apps.GetArgNullExString("steps"));
            }
        }
    }

    /// <summary>
    /// PipeStep
    /// </summary>
    /// <typeparam name="T">context data object type</typeparam>
    public sealed class PipeStep<T>
    {
        #region propertites

        private PipeStepStatus status = PipeStepStatus.Unknown;

        /// <summary>
        /// Status
        /// </summary>
        public PipeStepStatus Status
        {
            get { return status; }
        }

        private PipeStep<T> nextStep = null;

        /// <summary>
        /// NextStep
        /// </summary>
        public PipeStep<T> NextStep
        {
            get { return nextStep; }
            set { nextStep = value; }
        }

        private T contextData = default(T);

        /// <summary>
        /// ContentData
        /// </summary>
        public T ContextData
        {
            get { return contextData; }
            set { contextData = value; }
        }

        #endregion

        #region delegates

        private ProcessHandler process;
        /// <summary>
        /// Process
        /// </summary>
        public ProcessHandler Process
        {
            get { return process; }
            set { process = value; }
        }

        private Proc<T, bool> cancelProgressProc;
        /// <summary>
        /// CheckCancelProc
        /// </summary>
        public Proc<T, bool> CancelProgressProc
        {
            get { return cancelProgressProc; }
            set { cancelProgressProc = value; }
        }

        /// <summary>
        /// ProcessHandler
        /// </summary>
        /// <param name="ctx">context (generic type)</param>
        /// <returns>context instance</returns>
        public delegate T ProcessHandler(T ctx);

        private T OnProcess()
        {
            if (!object.Equals(process, null))
            {
                bool isCancel = false;
                if (!object.Equals(cancelProgressProc, null))
                {
                    isCancel = cancelProgressProc(contextData);
                }

                if (!isCancel)
                {
                    contextData = process(contextData);
                    status = PipeStepStatus.ProgressFinished;
                }
                else
                {
                    status = PipeStepStatus.ProgressCanceled;
                }
            }
            return contextData;
        }

        #endregion

        #region builders

        /// <summary>
        /// PipeStep
        /// </summary>
        /// <param name="ctx">context</param>
        public PipeStep(T ctx)
        {
            contextData = ctx;
            status = PipeStepStatus.Initialized;
        }

        /// <summary>
        /// PipeStep
        /// </summary>
        /// <param name="ctx">context</param>
        /// <param name="nxt">next step</param>
        public PipeStep(T ctx, PipeStep<T> nxt)
        {
            nextStep = nxt;
            contextData = ctx;
            status = PipeStepStatus.Initialized;
        }

        #endregion

        #region functions

        /// <summary>
        /// StartStep
        /// </summary>
        public void StartStep()
        {
            if (!(object.Equals(contextData, default(T)) || object.Equals(contextData, null)))
            {
                contextData = OnProcess();

                if (!object.Equals(nextStep, null))
                {
                    nextStep.ContextData = contextData;
                    nextStep.StartStep();
                }
                else
                {
                    status = PipeStepStatus.PipeComplete;
                }
            }
        }

        #endregion
    }
}